{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "from bertopic import BERTopic\n",
        "from sentence_transformers import SentenceTransformer\n",
        "from sklearn.feature_extraction.text import CountVectorizer\n",
        "from transformers.pipelines import pipeline\n",
        "from umap import UMAP\n",
        "from hdbscan import HDBSCAN\n",
        "from sklearn.feature_extraction.text import CountVectorizer"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# 加载数据"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "条数:  1000\n",
            "预览第一条:  文旅文 创看 洛阳 河南省 文旅文创 发展 大会 本次 大会 安排 项目 签约 主要 方面 内容 一是 文旅 产业 项目 签约 截至 目前 梳理 重点 文旅 项目 投资总额 525.6 亿元 遴选 重大项目 进行 现场 签约 投资总额 365.8 亿元 项目 包括 文物 数字化 开发 文化 创意 园区 建设 文化 项目 涵盖 旅游 度假区 建设 旅游 酒店 民宿 打造 旅游 项目 既有 旅游 景区 开发 商旅 综合体 建设 传统 业态 项目 宇宙 基地 沉浸 演艺 业态 项目 充分体现 我省 文化 旅游 发展 特点 趋势 二是 引客 入豫 项目 签约 主要 我省 文旅 部门 文旅 企业 头部 旅行 知名 OTA 平台 重点 客源地 文旅 部门 签订 引客 入豫 协议 持续 拓展 省外 客源 市场\n",
            "\n"
          ]
        }
      ],
      "source": [
        "# step1 加载文件\n",
        "with open('../../data/切词.txt', 'r', encoding='utf-8') as file:\n",
        "  docs = file.readlines()\n",
        "print('条数: ', len(docs))\n",
        "print('预览第一条: ', docs[0])\n",
        "\n",
        "vectorizer_model = None"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# 创建"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {},
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "Some weights of the model checkpoint at bert-base-chinese were not used when initializing BertModel: ['cls.predictions.transform.LayerNorm.bias', 'cls.predictions.decoder.weight', 'cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.transform.dense.bias', 'cls.seq_relationship.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.weight']\n",
            "- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
            "- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "(1000, 768)\n"
          ]
        }
      ],
      "source": [
        "# 1. 词向量模型，同时加载本地训练好的词向量\n",
        "embedding_model = pipeline(\"feature-extraction\", model=\"bert-base-chinese\") # 使用bert-base-chinese\n",
        "embeddings = np.load('../../data/embedding_bbc.npy') # 使用bert-base-chinese向量\n",
        "print(embeddings.shape)\n",
        "\n",
        "# 2. 创建分词模型\n",
        "vectorizer_model = CountVectorizer() # 因为我们已经分好词了，所以这里不需要传入分词函数了\n",
        "\n",
        "# 3. 创建UMAP降维模型\n",
        "umap_model = UMAP(\n",
        "  n_neighbors=15,\n",
        "  n_components=5,\n",
        "  min_dist=0.0,\n",
        "  metric='cosine',\n",
        "  random_state=42  # ⚠️ 防止随机 https://maartengr.github.io/BERTopic/faq.html\n",
        ")\n",
        "\n",
        "# 4. 创建HDBSCAN聚类模型\n",
        "# 如果要建设离群值，可以减小下面两个参数\n",
        "# https://hdbscan.readthedocs.io/en/latest/faq.html\n",
        "hdbscan_model = HDBSCAN(\n",
        "  min_cluster_size=10,\n",
        "  min_samples=5\n",
        ")\n",
        "\n",
        "# 5. 创建CountVectorizer模型\n",
        "vectorizer_model = CountVectorizer(stop_words=['洛阳', '旅游', '文化'])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "(5, 22158) (5, 768)\n"
          ]
        },
        {
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>Topic</th>\n",
              "      <th>Count</th>\n",
              "      <th>Name</th>\n",
              "      <th>Representation</th>\n",
              "      <th>Representative_Docs</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>-1</td>\n",
              "      <td>326</td>\n",
              "      <td>-1_景区_河南_城市_中国</td>\n",
              "      <td>[景区, 河南, 城市, 中国, 游客, 历史, 博物馆, 遗址, 洛阳市, 活动]</td>\n",
              "      <td>[河南 多家 景区 陆续 发布 开园 公告 台风 杜苏芮 强度 逐渐 减弱 河南 景区 陆续...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>0</td>\n",
              "      <td>348</td>\n",
              "      <td>0_河南_景区_旅行_活动</td>\n",
              "      <td>[河南, 景区, 旅行, 活动, 中国, 时间, 历史, 古城, 门票, 城市]</td>\n",
              "      <td>[洛邑 福利社 中国 韵味 中国 范儿 洛邑 古城 中国 韵味 中国 范儿 大型 汉服 活动...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>1</td>\n",
              "      <td>243</td>\n",
              "      <td>1_景区_发展_建设_游客</td>\n",
              "      <td>[景区, 发展, 建设, 游客, 项目, 河南, 国家, 城市, 河南省, 洛阳市]</td>\n",
              "      <td>[下午 起卡 影响 河南 河南 多家 景区 恢复 开放 台风 杜苏芮 昨天 停止 编号 带来...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>2</td>\n",
              "      <td>55</td>\n",
              "      <td>2_龙门石窟_中国_石窟_艺术</td>\n",
              "      <td>[龙门石窟, 中国, 石窟, 艺术, 世界, 文化遗产, 少林寺, 朝代, 造像, 位于]</td>\n",
              "      <td>[旅行 洛阳 龙门石窟 中国 石刻 艺术 宝库 现为 世界 文化遗产 全国 重点 文物保护 ...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>3</td>\n",
              "      <td>28</td>\n",
              "      <td>3_年票_旅客_郑州_列车</td>\n",
              "      <td>[年票, 旅客, 郑州, 列车, 客流, 出行, 景区, 发送, 假期, 高峰]</td>\n",
              "      <td>[今年 春运 洛阳 火车站 累计 发送 旅客 125.2 万人次 清明 长假 车票 开售 昨...</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "   Topic  Count             Name  \\\n",
              "0     -1    326   -1_景区_河南_城市_中国   \n",
              "1      0    348    0_河南_景区_旅行_活动   \n",
              "2      1    243    1_景区_发展_建设_游客   \n",
              "3      2     55  2_龙门石窟_中国_石窟_艺术   \n",
              "4      3     28    3_年票_旅客_郑州_列车   \n",
              "\n",
              "                                  Representation  \\\n",
              "0     [景区, 河南, 城市, 中国, 游客, 历史, 博物馆, 遗址, 洛阳市, 活动]   \n",
              "1       [河南, 景区, 旅行, 活动, 中国, 时间, 历史, 古城, 门票, 城市]   \n",
              "2     [景区, 发展, 建设, 游客, 项目, 河南, 国家, 城市, 河南省, 洛阳市]   \n",
              "3  [龙门石窟, 中国, 石窟, 艺术, 世界, 文化遗产, 少林寺, 朝代, 造像, 位于]   \n",
              "4       [年票, 旅客, 郑州, 列车, 客流, 出行, 景区, 发送, 假期, 高峰]   \n",
              "\n",
              "                                 Representative_Docs  \n",
              "0  [河南 多家 景区 陆续 发布 开园 公告 台风 杜苏芮 强度 逐渐 减弱 河南 景区 陆续...  \n",
              "1  [洛邑 福利社 中国 韵味 中国 范儿 洛邑 古城 中国 韵味 中国 范儿 大型 汉服 活动...  \n",
              "2  [下午 起卡 影响 河南 河南 多家 景区 恢复 开放 台风 杜苏芮 昨天 停止 编号 带来...  \n",
              "3  [旅行 洛阳 龙门石窟 中国 石刻 艺术 宝库 现为 世界 文化遗产 全国 重点 文物保护 ...  \n",
              "4  [今年 春运 洛阳 火车站 累计 发送 旅客 125.2 万人次 清明 长假 车票 开售 昨...  "
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "topic_model = BERTopic(\n",
        "  embedding_model=embedding_model,\n",
        "  vectorizer_model=vectorizer_model,\n",
        "  umap_model=umap_model,\n",
        "  hdbscan_model=hdbscan_model,\n",
        "  nr_topics=5 # https://maartengr.github.io/BERTopic/getting_started/topicreduction/topicreduction.html#manual-topic-reduction，这个缩减后的主题数是包含离群值的\n",
        ")\n",
        "\n",
        "topics = topic_model.fit_transform(docs, embeddings=embeddings) #传入训练好的词向量\n",
        "print(topic_model.c_tf_idf_.shape, topic_model.topic_embeddings_.shape)\n",
        "topic_info = topic_model.get_topic_info()\n",
        "topic_info"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# 在训练后缩减主题数"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>Topic</th>\n",
              "      <th>Count</th>\n",
              "      <th>Name</th>\n",
              "      <th>Representation</th>\n",
              "      <th>Representative_Docs</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>-1</td>\n",
              "      <td>326</td>\n",
              "      <td>-1_景区_河南_城市_中国</td>\n",
              "      <td>[景区, 河南, 城市, 中国, 游客, 历史, 博物馆, 遗址, 洛阳市, 活动]</td>\n",
              "      <td>[河南 多家 景区 陆续 发布 开园 公告 台风 杜苏芮 强度 逐渐 减弱 河南 景区 陆续...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>0</td>\n",
              "      <td>591</td>\n",
              "      <td>0_景区_河南_游客_活动</td>\n",
              "      <td>[景区, 河南, 游客, 活动, 城市, 中国, 洛阳市, 发展, 国家, 历史]</td>\n",
              "      <td>[河南 全部 旅游 景区 不得 擅自 开放 河南省 文化 旅游 了解 截止 全省 景区 景区...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>1</td>\n",
              "      <td>55</td>\n",
              "      <td>1_中国_龙门石窟_石窟_艺术</td>\n",
              "      <td>[中国, 龙门石窟, 石窟, 艺术, 世界, 文化遗产, 少林寺, 朝代, 造像, 位于]</td>\n",
              "      <td>[旅行 洛阳 龙门石窟 中国 石刻 艺术 宝库 现为 世界 文化遗产 全国 重点 文物保护 ...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>2</td>\n",
              "      <td>28</td>\n",
              "      <td>2_年票_旅客_郑州_列车</td>\n",
              "      <td>[年票, 旅客, 郑州, 列车, 客流, 出行, 景区, 发送, 假期, 高峰]</td>\n",
              "      <td>[今年 春运 洛阳 火车站 累计 发送 旅客 125.2 万人次 清明 长假 车票 开售 昨...</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "   Topic  Count             Name  \\\n",
              "0     -1    326   -1_景区_河南_城市_中国   \n",
              "1      0    591    0_景区_河南_游客_活动   \n",
              "2      1     55  1_中国_龙门石窟_石窟_艺术   \n",
              "3      2     28    2_年票_旅客_郑州_列车   \n",
              "\n",
              "                                  Representation  \\\n",
              "0     [景区, 河南, 城市, 中国, 游客, 历史, 博物馆, 遗址, 洛阳市, 活动]   \n",
              "1      [景区, 河南, 游客, 活动, 城市, 中国, 洛阳市, 发展, 国家, 历史]   \n",
              "2  [中国, 龙门石窟, 石窟, 艺术, 世界, 文化遗产, 少林寺, 朝代, 造像, 位于]   \n",
              "3       [年票, 旅客, 郑州, 列车, 客流, 出行, 景区, 发送, 假期, 高峰]   \n",
              "\n",
              "                                 Representative_Docs  \n",
              "0  [河南 多家 景区 陆续 发布 开园 公告 台风 杜苏芮 强度 逐渐 减弱 河南 景区 陆续...  \n",
              "1  [河南 全部 旅游 景区 不得 擅自 开放 河南省 文化 旅游 了解 截止 全省 景区 景区...  \n",
              "2  [旅行 洛阳 龙门石窟 中国 石刻 艺术 宝库 现为 世界 文化遗产 全国 重点 文物保护 ...  \n",
              "3  [今年 春运 洛阳 火车站 累计 发送 旅客 125.2 万人次 清明 长假 车票 开售 昨...  "
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# 也可以在训练后进行调用 https://maartengr.github.io/BERTopic/getting_started/topicreduction/topicreduction.html#manual-topic-reduction\n",
        "# 我这边测试的，和设置BERTopic的nr_topic参数效果是一样的\n",
        "topic_model.reduce_topics(docs, nr_topics=4)\n",
        "topic_model.get_topic_info()"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
